Selecting Shapes
This programming recipe adds a new feature to the application created in the previous recipe. With the code from this recipe, you can allow users to select a previously created path, as shown in Figure 6-2.Figure 6-2 Selecting a path shape
When the user presses the mouse button, the code in this recipe determines if the location was close to any previously created path. If so, the code deselects the previously selected path by erasing its geometry control handles, and selects the new path by drawing its geometry control handles.
If the place where the user clicks the mouse isn't close to any previously selected path, the recipe uses code from the previous recipe to allow the user
to create a new path.Overview of Recipe Steps
The steps in this recipe show you how to:
You need to follow the instructions in each step of this recipe to implement the path-selecting feature.
- Determine if the user is selecting a path
- Deselect the previously selected path
- Select the newly selected path
Functions Used in This Recipe
QuickDraw GX functions used in this recipe:
GXHitTestPicture
"Picture Shapes"
QuickDraw GX GraphicsGXSetShapeHitTest
"Transform Objects"
QuickDraw GX ObjectsGXDisposeShape
"Shape Objects"
QuickDraw GX ObjectsGXCloneShape
"Shape Objects"
QuickDraw GX ObjectsGXGetShapePoints
"Geometric Shapes"
QuickDraw GX GraphicsThis recipe gives a brief description of these functions; you can find complete reference information for these functions in the Inside Macintosh suite of books.
Recipe Step Descriptions
In this section, each step is described individually.
- Determine that the user is selecting a path
In the
MyContentClick
sample function presented in Step 3 of the previous recipe (on page 185), you converted the mouse location from QuickDraw global coordinates to QuickDraw GX local coordinates and then called theMyHandleCreatePath
sample function. For this recipe, you need to change the implementation of that function so that you can determine if the user is selecting an existing path rather than creating a new path. You can do this by calling a new sample function,MyHandleSelectPath
. Add these lines of code to yourMyContentClick
function:
boolean handled = false;
handled = MyHandleSelectPath(&localPoint);
if (! handled)
MyHandleCreatePath(&localPoint);The
MyHandleSelectPath
function determines whether an existing path shape was hit and, if so, it
- deselects the currently selected path
- selects the hit path
- returns
true
to indicate that the user was selecting a pathHere is the flow of control for the
MyHandleSelectPath
function:
boolean MyHandleSelectPath(gxPoint *hitPoint)
{
gxHitTestInfo result;
gxShape shapeHit;
if (gCurrent->pointCount == 0) {
shapeHit = GXHitTestPicture(gCurrent->picture,
hitPoint,
&result, 1, 1);
if ((result.what & gxGeometryPart) != 0) {
/* Deselect current path -- see Step 2. */
/* Select new path -- see Step 3. */
return true;
}
}
return false;
}This function first examines the current point count to be sure that it is 0. If the point count is not 0, then the user is in the middle of creating a new path and you don't want to allow them to select a previous one!
This function calls the
GXHitTestPicture
function to determine if any
path shape in the window's picture was hit by the mouse click. TheGXHitTestPicture
function returns a reference to the hit shape as
the function result and also returns information about the hit shape in theresult
parameter. Thewhat
field of this parameter describes what part
of the hit shape was hit.In this recipe, you are only interested in whether the user hit the geometry part of the path shape, so you can test the
what
field of theresult
parameter against the constantgxGeometryPart
. In fact, you can use the hit-test parameters property of the transform objects of the path shapes to specify that QuickDraw GX only consider the geometry parts of the path shapes when hit-testing them. You use theGXSetShapeHitTest
function to set a shape's hit-test parameters, as in this function call:
GXSetShapeHitTest(gCurrent->selectedPath,
gxGeometryPart,
ff(5));This function call specifies that the path should be hit-tested only on its geometry part and with a tolerance of 5.0.
You should set the hit-test parameters of your path shapes when you create them, in the
MyHandleCreatePath
function defined in the previous recipe.
- Deselect the previously selected path
Your
MyHandleSelectPath
function determines if the user is selecting a path. If so, you need to deselect the previously selected path, which you can do by erasing and disposing of its geometry control handles:
if (gCurrent->selectedPath != nil) {
MyErasePathControlHandles();
MyDisposePathControlHandles();
GXDisposeShape(gCurrent->selectedPath);
}Notice that this code calls the
GXDisposeShape
function on the currently selected path. This function lowers the owner count of that shape, but it does not actually free the memory used by the shape because there is still a reference to the shape in the window's picture.- Select the newly selected path
After you deselect the previously selected path, you can select the newly selected path by storing a reference to it in the document information structure:
gCurrent->selectedPath = GXCloneShape(result.which);This assignment statement copies a reference to the newly selected path shape into the
selectedPath
field of the document information structure. By calling theGXCloneShape
function, it also increments the owner count of this path shape to reflect the new reference to it. When you deselect this path shape (in Step 2), the owner count is decremented back to 1, reflecting the reference to the shape contained in the window picture.Now you can create and draw geometry control handles for the newly selected path:
MyCreatePathControlHandles(gCurrent->selectedPath);
MyDrawPathControlHandles();You can define a
MyCreatePathControlHandles
sample function to create all four geometry control handles with this code:
void MyCreatePathControlHandles(gxShape pathToSelect)
{
int index;
gxShape aControl;
gxPoint geometryPoints[kNumOfControls];
GXGetShapePoints(pathToSelect, 1,
kNumOfControls, geometryPoints);
for (index = 0; index <kNumOfControls; index++) {
MyCreateControlHandle(&aControl, &geometryPoints[index]);
gCurrent->controls[index] = aControl;
}
}This function uses the QuickDraw GX function
GXGetShapePoints
to fill an array with copies of the geometric points of the selected path. Then, the function uses theMyCreateControlHandle
function (which you defined on page 188) to create a geometry control handle for each geometric point in the path shape.
Related Recipes
The previous recipe, "Creating Geometric Shapes" beginning on page 179, shows how to allow the user to create path shapes. You must complete the steps of that recipe before using this recipe.The following two recipes show how to add more functionality to the path-
editing program:
The last recipe in this chapter, "Dragging Shapes Using Offscreen Bitmaps," beginning on page 217, shows how to allow the user to drag colored circles around a window. This recipe uses an offscreen bitmap to smooth the redrawing used for dragging feedback.
- "Editing Shape Geometries," described next, shows how to allow users
to redefine a previously created path by dragging on a geometry
control handle.- "Editing Shape Transforms," beginning on page 206, shows how to provide a different kind of control handle--a transform control handle. These control handles allow the user to edit the transform of the shape, rather than the geometry of the shape.
The recipes in Chapter 4, "Using the QuickDraw GX Environment," show you how to initialize QuickDraw GX and set up the QuickDraw GX debugging facilities. You should read the recipes in that chapter before using any recipes in this chapter.
The recipes in Chapter 5, "Using Macintosh Windows," show you how to create Macintosh windows, attach QuickDraw GX view ports to them, and implement zooming, resizing, and scrolling. You need to be familiar with the information in that chapter before you can display QuickDraw GX graphics
in a Macintosh window.The recipes in Chapter 6, "Handling Typography," show you how to create and manipulate typographic shapes.
The recipes in Chapter 8, "Printing," show you how to send your graphics and typographic images to a printer.